package com.zcj.demo.user.service;

import com.zcj.demo.mq.RabbitMqSender;
import com.zcj.demo.user.domain.UserDO;
import com.zcj.demo.user.repository.UserRepository;
import com.zcj.demo.user.service.dto.UserDTO;
import com.zcj.demo.user.service.mapper.UserMapper;
import com.zcj.demo.utils.RedisUtils;
import com.zcj.demo.utils.TokenUtil;
import com.zcj.demo.web.errors.AuthException;
import com.zcj.demo.web.errors.BusinessException;
import com.zcj.demo.web.user.vo.LoginVO;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 68300119
 * @Date 2020/10/13
 **/
@Service
public class AccountService implements UserDetailsService {

    private final UserRepository userRepository;

    private final RedisUtils redisUtils;

    private final RabbitMqSender rabbitMqSender;

    private final UserMapper userMapper;

    private final UserService userService;


    public AccountService(UserRepository userRepository,
                          RedisUtils redisUtils,
                          RabbitMqSender rabbitMqSender,
                          UserMapper userMapper,
                          UserService userService) {
        this.userRepository = userRepository;
        this.redisUtils = redisUtils;
        this.rabbitMqSender = rabbitMqSender;
        this.userMapper = userMapper;
        this.userService = userService;
    }

    /**
     * 根据用户名获取用户信息
     *
     * @param username 用户名
     * @return 用户信息
     */
    @Override
    public UserDO loadUserByUsername(String username) {
        UserDO user = null;
        if (StringUtils.isNotBlank(username)) {
            UserDTO userDTO = (UserDTO) redisUtils.get("login:" + username);
            user = userDTO != null ? userMapper.dtoToEntity(userDTO) : null;
        }
        if (ObjectUtils.isEmpty(user)) {
            user = userRepository.findByUsername(username);
        }
        if (ObjectUtils.isEmpty(user)) {
            throw new AuthException();
        }
        return user;
    }

    /**
     * 根据Token获取用户信息
     *
     * @param token token
     * @return 用户信息
     */
    public UserDO getSysAccountByToken(String token) {
        try {
            Map<String, Object> map = TokenUtil.getUserInfoFromToken(token);
            return loadUserByUsername(map.get("username").toString());
        } catch (UsernameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取验证码
     *
     * @param mobile 手机号
     * @return
     */
    public void getCaptcha(String mobile) {
        // 随机六位数字
        String code = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));
        // 发送短信 这里可以改成你自己的短信发送代码
        rabbitMqSender.sendSms(mobile, code);
        String securityCode = new BCryptPasswordEncoder().encode(code);
        redisUtils.set("captcha:" + mobile, securityCode, 600);
    }

    /**
     * 登陆
     * @param params 参数
     * @return token
     */
    public String login(LoginVO params) {
        if ("account".equals(params.getType())) {
            return this.loginByPassword(params);
        }
        if ("mobile".equals(params.getType())) {
            return this.loginByMobile(params);
        }
        return null;
    }

    /**
     * 通过账号密码登录
     *
     * @param params 登录信息
     * @return token
     */
    private String loginByPassword(LoginVO params) {
        UserDO account = this.loadUserByUsername(params.getUsername());

        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (bCryptPasswordEncoder.matches(params.getPassword(), account.getPassword())) {
            Map<String, Object> map = new HashMap<>(12);
            map.put("username", account.getUsername());
            redisUtils.set("login:" + account.getUsername(), userMapper.entityToDTO(account), TokenUtil.expiration);
            return TokenUtil.generateToken(map);
        }
        throw new AuthException();
    }

    /**
     * 通过手机登录
     *
     * @param params 登录信息
     * @return token
     */
    private String loginByMobile(LoginVO params) {
        UserDO account = userRepository.findByPhone(params.getMobile()).orElseThrow(AuthException::new);
        String code = (String) redisUtils.get("captcha:" + params.getMobile());
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (bCryptPasswordEncoder.matches(params.getCaptcha(), code)) {
            redisUtils.set("login:" + account.getUsername(), userMapper.entityToDTO(account), TokenUtil.expiration);
            Map<String, Object> map = new HashMap<>(12);
            map.put("username", account.getUsername());
            redisUtils.del("captcha:" + params.getMobile());
            return TokenUtil.generateToken(map);
        }
        throw new AuthException();
    }

    /**
     * 注册
     *
     * @param user 用户信息
     * @return 用户信息
     */
    public UserDTO register(UserDTO user) {
        if (!user.getPassword().equals(user.getConfirm())) {
            throw new BusinessException("两次密码输入不一致");
        }
        String captcha = (String) redisUtils.get("captcha:" + user.getPhone());
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bCryptPasswordEncoder.matches(user.getCaptcha(), captcha)) {
            throw new BusinessException("手机验证码错误");
        }
        UserDTO userDTO = userService.create(user);
        redisUtils.del("captcha:" + user.getPhone());
        return userDTO;
    }
}
